﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;

namespace Gmantis.Controls
{
    public class ZoomHelper
    {
        // Fields
        private bool _continuousZoom = true;
        private bool _ctrlRequired = true;
        private Point _cumulativeTranslation;
        private Point _cumulativeZoom;
        private UIElement _element;
        private System.Windows.RoutedEventArgs _firstEventArgs;
        private bool _handleMouseWheel = true;
        private PointerDeviceType _pointerType;
        private Stack<KeyValuePair<DateTime, Point>> _points = new Stack<KeyValuePair<DateTime, Point>>();
        private Point _targetZoom;
        private bool _zoomStarted;

        // Events
        public event EventHandler<ZoomCompletedEventArgs> ZoomCompleted;

        public event EventHandler<ZoomDeltaEventArgs> ZoomDelta;

        public event EventHandler<ZoomStartedEventArgs> ZoomStarted;

        // Methods
        public ZoomHelper(UIElement element, bool ctrlRequired = true, bool continuousZoom = true, bool handleMouseWheel = true)
        {
            this._element = element;
            this._handleMouseWheel = handleMouseWheel;
            this._continuousZoom = continuousZoom;
            this._ctrlRequired = ctrlRequired;
            this.InitializeZoom();
        }

        internal void Complete(System.Windows.RoutedEventArgs originalArgs = null)
        {
            if (this._zoomStarted)
            {
                this._zoomStarted = false;
                if (this._continuousZoom)
                {
                    CompositionTarget.Rendering -= new EventHandler(this.OnRendering);
                }
                this.RaiseZoomCompleted(originalArgs, this._cumulativeZoom, this._cumulativeTranslation);
            }
        }

        public void FinalizeZoom()
        {
            this._element.MouseWheel -= new MouseWheelEventHandler(this.OnMouseWheel);
        }

        private Point GetFinalVelocities()
        {
            if (this._points.Count >= 2)
            {
                KeyValuePair<DateTime, Point> pair = this._points.Pop();
                KeyValuePair<DateTime, Point> pair2 = this._points.Pop();
                DateTime key = pair.Key;
                DateTime time2 = pair2.Key;
                TimeSpan span = (TimeSpan)(key - time2);
                long ticks = span.Ticks;
                if ((ticks > 0L) && ((DateTime.Now - key) < TimeSpan.FromMilliseconds(100.0)))
                {
                    Point point = pair.Value;
                    Point point2 = pair2.Value;
                    return new Point(((point.X - point2.X) * 10000000.0) / ((double)ticks), ((point.Y - point2.Y) * 10000000.0) / ((double)ticks));
                }
            }
            return new Point();
        }

        private void InitializeZoom()
        {
            this._zoomStarted = false;
            this._element.MouseWheel += new MouseWheelEventHandler(this.OnMouseWheel);
        }

        private void OnMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
        {
            if (!this._ctrlRequired || KeyboardUtil.Ctrl)
            {
                if (!this._zoomStarted)
                {
                    this.Start(e);
                }
                double x = Math.Pow(2.0, ((double)e.Delta) / 240.0);
                if (this._continuousZoom)
                {
                    this._targetZoom = new Point(this._targetZoom.X * x, this._targetZoom.Y * x);
                }
                else
                {
                    Point deltaScale = new Point(x, x);
                    this._cumulativeZoom = new Point(this._cumulativeZoom.X * deltaScale.X, this._cumulativeZoom.Y * deltaScale.Y);
                    e.GetPosition(null);
                    this.RaiseZoomDelta(e, this._cumulativeZoom, deltaScale, new Point(), new Point());
                    this.Complete(e);
                }
                if (this._handleMouseWheel)
                {
                    e.Handled = true;
                }
            }
        }

        private void OnRendering(object sender, EventArgs e)
        {
            double x = Math.Pow(2.0, Math.Log(this._targetZoom.X / this._cumulativeZoom.X, 2.0) / 10.0);
            double y = Math.Pow(2.0, Math.Log(this._targetZoom.Y / this._cumulativeZoom.Y, 2.0) / 10.0);
            Point deltaScale = new Point(x, y);
            this._cumulativeZoom = new Point(this._cumulativeZoom.X * deltaScale.X, this._cumulativeZoom.Y * deltaScale.Y);
            this.RaiseZoomDelta(this._firstEventArgs, this._cumulativeZoom, deltaScale, this._cumulativeTranslation, new Point());
            if ((Math.Abs(Math.Log(this._targetZoom.X / this._cumulativeZoom.X, 2.0)) < 0.1) && (Math.Abs(Math.Log(this._targetZoom.Y / this._cumulativeZoom.Y, 2.0)) < 0.1))
            {
                this.Complete(null);
            }
        }

        private void RaiseZoomCompleted(System.Windows.RoutedEventArgs originalArgs, Point cumulativeScale, Point cumulativeTranslation)
        {
            if (this.ZoomCompleted != null)
            {
                this.ZoomCompleted(this._element, new ZoomCompletedEventArgs(this, originalArgs, this._pointerType, cumulativeScale, cumulativeTranslation));
            }
        }

        private void RaiseZoomDelta(System.Windows.RoutedEventArgs originalArgs, Point cumulativeScale, Point deltaScale, Point cumulativeTranslation, Point deltaTranslation)
        {
            if (this.ZoomDelta != null)
            {
                this.ZoomDelta(this._element, new ZoomDeltaEventArgs(this, originalArgs, this._pointerType, deltaScale, cumulativeScale, deltaTranslation, cumulativeTranslation));
            }
        }

        private void RaiseZoomInertiaStarted(RoutedEventArgs originalArgs, Point velocity)
        {
        }

        private void RaiseZoomStarted(System.Windows.RoutedEventArgs originalArgs)
        {
            if (this.ZoomStarted != null)
            {
                this.ZoomStarted(this._element, new ZoomStartedEventArgs(this, originalArgs, this._pointerType));
            }
        }

        private void Start(System.Windows.RoutedEventArgs originalArgs)
        {
            if (this._zoomStarted)
            {
                this.Complete(originalArgs);
            }
            this._cumulativeZoom = new Point(1.0, 1.0);
            this._targetZoom = new Point(1.0, 1.0);
            this._cumulativeTranslation = new Point();
            this._zoomStarted = true;
            this._pointerType = InputEventArgs.GetPointerType(originalArgs);
            this._firstEventArgs = originalArgs;
            this.RaiseZoomStarted(originalArgs);
            if (this._continuousZoom && (this._pointerType == PointerDeviceType.Mouse))
            {
                CompositionTarget.Rendering += new EventHandler(this.OnRendering);
            }
        }
    }
}
